package com.morningstar.flow.service;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.morningstar.common.result.Result;
import com.morningstar.flow.controller.request.StartProcessEntity;
import com.morningstar.flow.dao.entity.FlowInstance;
import com.morningstar.flow.dao.mapper.FlowInstanceMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.event.impl.FlowableProcessTerminatedEventImpl;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.morningstar.common.result.ResultCode.FLOW_NOT_EXIST;
import static com.morningstar.flow.constants.FlowStatus.*;
import static com.morningstar.flow.flowable.constants.VariableName.NAME_AUDIT;
import static com.morningstar.flow.flowable.constants.VariableName.START_USER;

@Slf4j
@Service
public class FlowInstanceService extends ServiceImpl<FlowInstanceMapper, FlowInstance> {

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private FlowInstanceMapper flowInstanceMapper;

    @Resource
    private FlowInstanceTimeLineService flowInstanceTimeLineService;

    /**
     * 草稿
     *
     * @param startProcessEntity 发起流程实体
     */
    public void draft(StartProcessEntity startProcessEntity) {
        // 更新
        if (startProcessEntity.getInstanceId() != null) {
            FlowInstance flowInstance = this.getById(startProcessEntity.getInstanceId());
            flowInstance.setFormModel(startProcessEntity.getFormModel());
            flowInstanceMapper.updateById(flowInstance);
            return;
        }
        // 新增
        String userId = StpUtil.getLoginId().toString();
        FlowInstance flowInstance = new FlowInstance();
        flowInstance.setProcessId(Long.parseLong(startProcessEntity.getProcessId()));
        // TODO 业务编号实现策略
        flowInstance.setBizNumber("FLOW" + System.currentTimeMillis());
        flowInstance.setFormModel(startProcessEntity.getFormModel());
        flowInstance.setStartUserId(Long.parseLong(userId));
        // 流程状态
        flowInstance.setStatus(FLOW_STATUS_DRAFT);
        flowInstanceMapper.insert(flowInstance);
    }

    @Transactional(rollbackFor = Exception.class)
    public String start(StartProcessEntity startProcessEntity) {
        String userId = StpUtil.getLoginId().toString();
        log.info("start process:{} {}", userId, JSON.toJSONString(startProcessEntity));
        Map<String, Object> map = new HashMap<>();
        map.put(START_USER, userId);
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(startProcessEntity.getProcessKey(), map);
        flowInstanceTimeLineService.processStart(instance.getId(), Long.parseLong(userId), startProcessEntity.getProcessKey());
        if (startProcessEntity.getInstanceId() != null) {
            FlowInstance flowInstance = this.getById(startProcessEntity.getInstanceId());
            flowInstance.setFormModel(startProcessEntity.getFormModel());
            flowInstance.setProcessInstanceId(instance.getId());
            flowInstance.setStatus(FLOW_STATUS_ING);
            this.updateById(flowInstance);
            return instance.getId();
        }
        FlowInstance flowInstance = new FlowInstance();
        flowInstance.setProcessId(Long.parseLong(startProcessEntity.getProcessId()));
        flowInstance.setProcessInstanceId(instance.getId());
        // TODO 业务编号实现策略
        flowInstance.setBizNumber("FLOW" + System.currentTimeMillis());
        flowInstance.setFormModel(startProcessEntity.getFormModel());
        flowInstance.setStartUserId(Long.parseLong(userId));
        flowInstance.setStatus(FLOW_STATUS_ING);
        flowInstanceMapper.insert(flowInstance);
        return instance.getId();
    }

    /**
     * 撤销流程
     *
     * @param processInstanceId 流程实例Id
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> revocation(String processInstanceId) {
        String userId = StpUtil.getLoginId().toString();
        // 查询流程实列是否在运行中
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance == null) {
            return Result.fail(FLOW_NOT_EXIST);
        }
        // 删除流程实列
        runtimeService.deleteProcessInstance(processInstanceId, "撤销流程");
        LambdaUpdateWrapper<FlowInstance> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(FlowInstance::getProcessInstanceId, processInstanceId)
                .eq(FlowInstance::getStatus, FLOW_STATUS_ING)
                .set(FlowInstance::getStatus, FLOW_STATUS_DESTROY)
                .set(FlowInstance::getEndTime, LocalDateTime.now());
        this.update(updateWrapper);
        flowInstanceTimeLineService.processRevocation(processInstanceId, Long.parseLong(userId), null);
        return Result.successNoData();
    }

    /**
     * 流程结束
     *
     * @param terminatedEvent 流程结束事件
     */
    @Transactional(rollbackFor = Exception.class)
    public void end(FlowableProcessTerminatedEventImpl terminatedEvent) {
        String processInstanceId = terminatedEvent.getProcessInstanceId();
        Object variable = terminatedEvent.getExecution().getVariable(NAME_AUDIT);
        Boolean approveResult = Convert.toBool(variable);
        LambdaUpdateWrapper<FlowInstance> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(FlowInstance::getProcessInstanceId, processInstanceId)
                .eq(FlowInstance::getStatus, FLOW_STATUS_ING)
                .set(FlowInstance::getStatus, approveResult ? FLOW_STATUS_PASS : FLOW_STATUS_REJECT)
                .set(FlowInstance::getEndTime, LocalDateTime.now());
        this.update(updateWrapper);
        flowInstanceTimeLineService.processEnd(processInstanceId, terminatedEvent.getProcessDefinitionId());
    }

    /**
     * 根据流程实例Id查询流程实例
     * @param processInstanceId 流程实例信息
     * @return 流程实例信息
     */
    public FlowInstance queryByProcessInstanceId(String processInstanceId) {
        LambdaQueryWrapper<FlowInstance> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FlowInstance::getProcessInstanceId, processInstanceId);
        return this.getOne(queryWrapper);
    }

    /**
     * 根据流程实例Id查询流程实例
     *
     * @param instanceIdList 流程实例Id列表
     * @return 流程实例
     */
    public List<FlowInstance> queryByInstanceIds(List<String> instanceIdList) {
        LambdaQueryWrapper<FlowInstance> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(FlowInstance::getProcessInstanceId, instanceIdList);
        return this.list(queryWrapper);
    }

    /**
     * 根据流程实例Id查询流程实例
     *
     * @param instanceIdList 流程实例Id Map
     * @return 流程实例
     */
    public Map<String, FlowInstance> queryMapByInstanceIds(List<String> instanceIdList) {
        return this.queryByInstanceIds(instanceIdList)
                .stream()
                .collect(Collectors.toMap(FlowInstance::getProcessInstanceId, flowInstance -> flowInstance));
    }
}
